local jass = require 'jass.common'
local japi = require 'jass.japi'
local msg  = require 'jass.message'

local mover = { }
setmetatable(mover,mover)
ac.mover.class['target'] = mover

-- 转向速度
mover.velocity = 720

-- 平滑转向
function mover.jog_turn(start,target)
    local angle, direction = ac.math_angle(start, target) -- 夹角 反向
    return math.lerp(start, target, mover.velocity / angle)
end 

mover.target_handle_list = {}

local mt = { }
mover.__index = mt 
extends(ac.mover)(mt)

-- 起始点
mt.start = nil 

-- 目标
mt.target = nil

-- 弧度
mt.arc = 0.00

-- 追踪
mt.homing = true

-- 速度
mt.speed = 1000

-- 是否旋转倾角
mt.pitch_flag = true 

--单位死亡自动删除
mt.auto_remove = true

function mt:init(has)

    if not self.target then 
        log.error('没有传入目标')
        return
    end 
    
    self.start = self.start or self.mover and self.mover:get_point()
    if not self.start then 
        log.error('没有传入起始点')
        return
    end 
    if has==nil then
        ac.mover.__index.init(self)
    end

    local start = self.start

    local target = self.target:get_impact_point()
    if start.type=='point' and start.z==0 then 
        start.z = start.z + start:get_height() + self.height
    end 
    
    self.launch_point = start
    self.impact_point = target
    self.distance = start * target
    self.height = self.distance * (self.arc) + math.lerp(self.launch_point.z, self.impact_point.z, 0.5)
    self.length = math.sqrt((self.launch_point.z - self.impact_point.z) ^ 2 + (self.launch_point * self.impact_point) ^ 2)
    self.has_hit = true
    self.has_revise = true
    self.has_homing = self.homing
    self.moved = 0
    self.mover:set_point(self.launch_point)
    self.mover:set_height(self.launch_point.z)

    if self.auto_remove then
        local movers = self.target.movers 
        if not movers then 
            movers = {}
            self.target.movers = movers
        end
        table.insert(self.target.movers,self)
    end
end 

function ac.unit.__index:remove_movers()
    local movers = self.movers 
    if movers then 
        for i = #movers, 1, -1 do
            movers[i]:remove()
        end
    end
    self.movers = nil 
end 

-- 修正落点
function mt:revise()
    if self.has_revise then -- 如果能修正
        -- 计算出偏移的距离
        local target = self.target
        local this_launch_point = self.launch_point
        local this_impact_point = self.impact_point
        local real_impact_point = target:get_impact_point()
        local deviation = math.sqrt((this_impact_point.z - real_impact_point.z) ^ 2 + (this_impact_point * real_impact_point) ^ 2)
        if deviation <= (522 * self.frame) then --  则判断是否满足修正的条件
            -- 进行修正
            self.height = this_launch_point * real_impact_point * (self.arc) + math.lerp(this_launch_point.z, real_impact_point.z, 0.5)
            self.length = math.sqrt((this_launch_point.z - real_impact_point.z) ^ 2 + (this_launch_point * real_impact_point) ^ 2)
            self.impact_point = real_impact_point
        else
            self.has_revise = false
        end
    end 
    return self.has_revise
end

-- 移动
function mt:move()

    if self.length == 0 then
        self:hit()
        return
    end 

    if not self:revise() and self.has_homing then 
        self:move_homing() 
    else
        self:move_normal()
    end

end 

-- 丢失 不会再追踪和触发命中
function mt:lose()
    self.has_homing = false
    self.has_hit = false
end 

-- 命中
function mt:hit()
    if self.has_hit then 
        self:notify('on_hit', self.target)
    end 
    self:finish()
end 

-- 停止
function mt:finish()
    ac.mover.__index.finish(self)
end

-- 删除 
function mt:remove()
    local movers = self.target.movers 
    if movers then 
        for i = #movers, 1, -1 do
            if movers[i] == self then
                table.remove(movers, i)
                break
            end
        end
    end
    ac.mover.__index.remove(self)
end 

-- 常规移动
function mt:move_normal()

    local launch_point = self.launch_point
    local impact_point = self.impact_point
    local height = self.height
    local object = self.mover

    if self.speed <= 0 then 
        self.moved = self.length 
    else
        self.moved = self.moved + self.speed * self.frame
    end 
    
    local progress = math.min(self.moved / self.length, 1)
    local this_point = object:get_point()
    local next_point = launch_point:lerp( impact_point, progress)
    local this_height = object:get_height()
    local lerp_height = math.lerp(launch_point.z, impact_point.z, progress)
    local next_height = self.arc ~= 0 and math.parabola( launch_point.z ,height, impact_point.z, progress) or lerp_height

    local next_angle = this_point / impact_point
    local next_pitch = ( (progress < 1) and self.pitch_flag) and math.atan( this_height - next_height, math.sqrt( (this_point.x - next_point.x) ^ 2 + (this_point.y - next_point.y) ^ 2 ) ) or 0

    if object.type == 'unit' then 
        next_height = next_height - next_point:get_height()
    end 
    -- 理论上 不需要空中单位支持 如果需要就从这里下手改
    if self:notify ('on_move', next_point, next_height) == false then 
        self:finish()
        return  
    end 

    self:set_point(next_point)
    self:set_height(next_height)
    self:set_rotate(0, next_pitch, next_angle)

    if progress == 1 then 
        self:hit()
    end 
    
end 

-- 追踪移动
function mt:move_homing()

    local target = self.target
    local object = self.mover
    
    if self.has_homing then 
        self.impact_point = target:get_impact_point()
    end

    local launch_point = self.launch_point
    local impact_point = self.impact_point

    local this_point = self:get_point()
    local this_height = self:get_height()
    local _, this_pitch, this_angle = self:get_rotate()

    self.length = math.sqrt((this_height - impact_point.z) ^ 2 + (this_point * impact_point) ^ 2)
    local progress

    if self.speed <= 0 then 
        self.moved = self.length 
        progress = 1
    else
        self.moved = self.moved + self.speed * self.frame
        progress = math.min(self.speed * self.frame / self.length, 1)
    end 

    local next_point = this_point:lerp( impact_point, progress)
    local next_height = math.lerp(this_height, impact_point.z, progress)

    local next_angle = this_point / impact_point
    local next_pitch = ( (progress < 1) and self.pitch_flag) and math.atan( this_height - next_height, math.sqrt( (this_point.x - next_point.x) ^ 2 + (this_point.y - next_point.y) ^ 2 ) ) or 0

    if object.type == 'unit' then 
        next_height = next_height - next_point:get_height()
    end 

    if self:notify ('on_move', next_point, next_height) == false then 
        self:finish()
        return  
    end 

    self:set_point(next_point)
    self:set_height(next_height)
    self:set_rotate(0, next_pitch, next_angle)

    if progress == 1 then 
        self:hit()
    end 
end 

return mover